﻿// ———————————————————————–
// <copyright file="KMLStyle.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// KMLStyle.cs - "Contains a series of methods for parsing and formatting KML docuements."
// Project: "ICNETSOA"- "ICNETDAL"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using ICNETServices;

namespace ICNET_KMLStyleEngine
{
  #region Public Enums

  /// <summary>
  /// The fields of this enum correspond to how a feature is displayed in list view. 
  /// </summary>
  public enum ListItemTypeEnum
  {
    /// <summary>
    /// The feature's visibility is tied to the item checkbox.
    /// </summary>
    check,

    /// <summary>
    /// Prevents the user from turning visibility on/off on all itmes at once.
    /// </summary>
    checkOffOnly,

    /// <summary>
    /// Use a normal check box for visibility but do not display child directories (visibility of child directories can
    /// be toggled via checkbox.
    /// </summary>
    checkHideChildren,

    /// <summary>
    /// Allows only one item in a container to be visible at a time. 
    /// </summary>
    radioFolder
  }

  /// <summary>
  /// These enums correspond to the icon used to represent the state of a folder
  /// </summary>
  public enum ItemIconModeEnum
  {
    /// <summary>
    /// The folder is open 
    /// </summary>
    open,

    /// <summary>
    /// The folder is closed
    /// </summary>
    closed,

    /// <summary>
    /// There was an error accessing the folder (can be combined with open or closed)
    /// </summary>
    error,

    /// <summary>
    /// The folder is being fetched (ball representing loading position is on the bottom left of the folder)
    /// </summary>
    fetching0,

    /// <summary>
    /// The folder is being fetched (ball representing loading position is on the middle of the folder)
    /// </summary>
    fetching1,

    /// <summary>
    /// The folder is being fetched (ball representing loading position is on the bottom right  of the folder)
    /// </summary>
    fetching2
  }

  /// <summary>
  /// Corresponds to the display mode used by Google Earth
  /// </summary>
  public enum DisplayModeEnum
  {
    /// <summary>
    /// Default display mode, uses the information in the text field to create a balloon.
    /// </summary>
    [XmlEnum("default")]
    _default,

    /// <summary>
    /// Does not create a balloon.
    /// </summary>
    hide
  }

  /// <summary>
  /// Corresponds to the color mode used in the style. 
  /// </summary>
  public enum ColorModeEnum
  {
    /// <summary>
    /// The default color.
    /// </summary>
    normal,

    /// <summary>
    /// A random color.
    /// </summary>
    random
  }

  /// <summary>
  /// Corresponds to when the display will be refreshed
  /// </summary>
  public enum RefreshModeEnum
  {
    /// <summary>
    /// When the file is loaded or changed.
    /// </summary>
    onChange,

    /// <summary>
    /// Every x seconds.
    /// </summary>
    onInterval,

    /// <summary>
    /// When an expiratoin time is reached.
    /// </summary>
    onExpire
  }

  /// <summary>
  /// Corresponds to how the link is refreshed when the camera is changed.
  /// </summary>
  public enum ViewRefreshModeEnum
  {
    /// <summary>
    /// Does not change when the camera is changed.
    /// </summary>
    never,

    /// <summary>
    /// X seconds after the camera stops moving 
    /// </summary>
    onStop,

    /// <summary>
    /// When the users requests a refresh.
    /// </summary>
    onRequest,

    /// <summary>
    /// When a new region becomes active.
    /// </summary>
    onRegion
  }

  /// <summary>
  /// Represents the ways in which x and y values for HotSpot can be specified
  /// </summary>
  public enum HotSpotUnitEnum
  {
    /// <summary>
    /// Specified with a number of pixels
    /// </summary>
    pixels,

    /// <summary>
    /// Specified as a fractional component of the image
    /// </summary>
    fraction,

    /// <summary>
    /// Specified as an indent from the top/right side of the image. 
    /// </summary>
    insetPixels
  }
  #endregion

  /// <summary>
  /// Contains methods to aid in the serializing and deserializing XML found in the Style section of KML
  /// </summary>
  public class KMLStyle : IXmlSerializable
  {
    #region Private Member Variables

    /// <summary>
    /// The unique ID of the schema 
    /// </summary>
    private string id;

    /// <summary>
    /// The display name formatted with spaces and punctuation. 
    /// </summary>
    private string friendlyname;

    /// <summary>
    /// The IconStyle associated with this style
    /// </summary>
    private IconStyle iconstyle;

    /// <summary>
    /// The LabelStyle associated with this style
    /// </summary>
    private LabelStyle labelstyle;

    /// <summary>
    /// The LineStyle associated with this style
    /// </summary>
    private LineStyle linestyle;

    /// <summary>
    /// The PolyStyle associated with this style
    /// </summary>
    private PolyStyle polystyle;

    /// <summary>
    /// The BalloonStyle associated with this style
    /// </summary>
    private BalloonStyle balloonstyle;

    /// <summary>
    /// The ListStyle associated with this style
    /// </summary>
    private ListStyle liststyle;
    #endregion

    #region Public Accessors

    /// <summary>
    /// Gets the ID
    /// </summary>
    [XmlIgnore]
    public string ID
    {
      get
      {
        return this.id;
      }
    }

    /// <summary>
    /// Gets the friendly name
    /// </summary>
    [XmlIgnore]
    public string FriendlyName
    {
      get
      {
        return this.friendlyname;
      }
    }
    #endregion

    #region Public Member Functions

    /// <summary>
    /// Parses a string and attempts to convert it to a ListItemType enum
    /// </summary>
    /// <param name="listItemVal">The value to be converted</param>
    /// <param name="returnValue">The ListItemType object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string listItemVal, out ListItemTypeEnum returnValue)
    {
      try
      {
        ListItemTypeEnum val = (ListItemTypeEnum)Enum.Parse(typeof(ListItemTypeEnum), listItemVal, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = ListItemTypeEnum.check;
        return false;
      }
    }

    /// <summary>
    /// Parses a string and attempts to convert it to a ItemIconMode enum.
    /// </summary>
    /// <param name="itemIconValue">The value to be converted.</param>
    /// <param name="returnValue">The ItemIconMode object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string itemIconValue, out ItemIconModeEnum returnValue)
    {
      try
      {
        ItemIconModeEnum val = (ItemIconModeEnum)Enum.Parse(typeof(ItemIconModeEnum), itemIconValue, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = ItemIconModeEnum.closed;
        return false;
      }
    }

    /// <summary>
    /// Parses a string and attempts to convert it to a DisplayMode enum.
    /// </summary>
    /// <param name="displayModeValue">The value to be converted.</param>
    /// <param name="returnValue">The DisplayMode object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string displayModeValue, out DisplayModeEnum returnValue)
    {
      try
      {
        DisplayModeEnum val = (DisplayModeEnum)Enum.Parse(typeof(DisplayModeEnum), displayModeValue, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = DisplayModeEnum._default;
        return false;
      }
    }

    /// <summary>
    /// Parses a string and attempts to convert it to a ColorMode enum.
    /// </summary>
    /// <param name="colorModeValue">The value to be converted.</param>
    /// <param name="returnValue">The ItemIconMode object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string colorModeValue, out ColorModeEnum returnValue)
    {
      try
      {
        ColorModeEnum val = (ColorModeEnum)Enum.Parse(typeof(ColorModeEnum), colorModeValue, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = ColorModeEnum.normal;
        return false;
      }
    }

    /// <summary>
    /// Parses a string and attempts to convert it to a RefreshMode enum.
    /// </summary>
    /// <param name="refreshModeValue">The value to be converted.</param>
    /// <param name="returnValue">The RefreshMode object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string refreshModeValue, out RefreshModeEnum returnValue)
    {
      try
      {
        RefreshModeEnum val = (RefreshModeEnum)Enum.Parse(typeof(RefreshModeEnum), refreshModeValue, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = RefreshModeEnum.onChange;
        return false;
      }
    }

    /// <summary>
    /// Parses a string and attempts to convert it to a ViewRefreshMode enum.
    /// </summary>
    /// <param name="viewRefreshModeValue">The value to be converted.</param>
    /// <param name="returnValue">The ViewRefreshMode object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string viewRefreshModeValue, out ViewRefreshModeEnum returnValue)
    {
      try
      {
        ViewRefreshModeEnum val = (ViewRefreshModeEnum)Enum.Parse(typeof(ViewRefreshModeEnum), viewRefreshModeValue, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = ViewRefreshModeEnum.never;
        return false;
      }
    }

    /// <summary>
    /// Parses a string and attempts to convert it to a HotSpotUnit enum.
    /// </summary>
    /// <param name="hotSpotUnitValue">The value to be converted.</param>
    /// <param name="returnValue">The HotSpotUnit object to which the parsed string will be stored if it is successfully converted</param>
    /// <returns>True if the conversion was successful, false if it was not.</returns>
    public static bool EnumTryParse(string hotSpotUnitValue, out HotSpotUnitEnum returnValue)
    {
      try
      {
        HotSpotUnitEnum val = (HotSpotUnitEnum)Enum.Parse(typeof(HotSpotUnitEnum), hotSpotUnitValue, true);
        returnValue = val;
        return true;
      }
      catch
      {
        returnValue = HotSpotUnitEnum.pixels;
        return false;
      }
    }

    /// <summary>
    /// Converts a href from a relative path to an absolute path.
    /// </summary>
    /// <param name="hypertextRef">The href which will be modified</param>
    public static void SetHRef(ref string hypertextRef)
    {
      if (hypertextRef.StartsWith("/"))
      {
        if (string.IsNullOrWhiteSpace(ICNETSOAConstants.ResourcePort))
        {
          hypertextRef = ICNETSOAConstants.ICNETbaseURI + hypertextRef;
        }
        else
        {
          hypertextRef = ICNETSOAConstants.ICNETbaseURI + ":" + ICNETSOAConstants.ResourcePort + hypertextRef;
        }
      }
    }

    /// <summary>
    /// Moves an XML reader to the nearest content node that contains text.
    /// </summary>
    /// <param name="theReader">The object that will read the xml</param>
    /// <returns>Returns null.</returns>
    public static string GetElementStringValue(XmlReader theReader)
    {
      theReader.Read(); // initialize to source node

      if (theReader.Read())
      {
        if (theReader.MoveToContent() == XmlNodeType.Text)
        {
          return theReader.Value;
        }
        else
        {
          return null;
        }
      }
      else
      {
        return null;
      }
    }

    /// <summary>
    /// Attempts to parse a string and convert it into a float.
    /// </summary>
    /// <param name="value">The string to be parsed. </param>
    /// <returns>The parsed value.</returns>
    public static float? ParseFloat(string value)
    {
      bool canParse;
      float val;

      canParse = float.TryParse(value, out val);

      if (canParse)
      {
        return val;
      }
      else
      {
        return null;
      }
    }

    /// <summary>
    /// Attempts to parse a string and convert it into a int.
    /// </summary>
    /// <param name="value">The string to be parsed. </param>
    /// <returns>The parsed value.</returns>
    public static int? ParseInt(string value)
    {
      bool canParse;
      int val;

      canParse = Int32.TryParse(value, out val);

      if (canParse)
      {
        return val;
      }
      else
      {
        return null;
      }
    }

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;
      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.iconstyle != null && this.iconstyle.HasAValue())
      {
        hasAValue = true;
      }

      if (this.labelstyle != null && this.labelstyle.HasAValue())
      {
        hasAValue = true;
      }

      if (this.linestyle != null && this.linestyle.HasAValue())
      {
        hasAValue = true;
      }

      if (this.polystyle != null && this.polystyle.HasAValue())
      {
        hasAValue = true;
      }

      if (this.balloonstyle != null)
      {
        hasAValue = true;
      }

      if (this.liststyle != null && this.liststyle.HasAValue())
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the Style section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "Style")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];
          this.friendlyname = reader["friendlyname"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadStyleXML(innerReader);

          // reader.Read();
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadStyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "IconStyle")
            {
              this.iconstyle = new IconStyle();

              this.iconstyle.ReadXml(reader);

              if (!this.iconstyle.HasAValue())
              {
                this.iconstyle = null;
              }
            }
            else if (reader.LocalName == "LabelStyle")
            {
              this.labelstyle = new LabelStyle();

              this.labelstyle.ReadXml(reader);

              if (!this.labelstyle.HasAValue())
              {
                this.labelstyle = null;
              }
            }
            else if (reader.LocalName == "LineStyle")
            {
              this.linestyle = new LineStyle();

              this.linestyle.ReadXml(reader);

              if (!this.linestyle.HasAValue())
              {
                this.linestyle = null;
              }
            }
            else if (reader.LocalName == "PolyStyle")
            {
              this.polystyle = new PolyStyle();

              this.polystyle.ReadXml(reader);

              if (!this.polystyle.HasAValue())
              {
                this.polystyle = null;
              }
            }
            else if (reader.LocalName == "BalloonStyle")
            {
              this.balloonstyle = new BalloonStyle();

              this.balloonstyle.ReadXml(reader);

              if (!this.balloonstyle.HasAValue())
              {
                this.balloonstyle = null;
              }
            }
            else if (reader.LocalName == "ListStyle")
            {
              this.liststyle = new ListStyle();

              this.liststyle.ReadXml(reader);

              if (!this.liststyle.HasAValue())
              {
                this.liststyle = null;
              }
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      writer.WriteAttributeString("id", this.id);

      if (this.iconstyle != null)
      {
        writer.WriteStartElement("IconStyle");
        this.iconstyle.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.labelstyle != null)
      {
        writer.WriteStartElement("LabelStyle");
        this.labelstyle.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.linestyle != null)
      {
        writer.WriteStartElement("LineStyle");
        this.linestyle.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.polystyle != null)
      {
        writer.WriteStartElement("PolyStyle");
        this.polystyle.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.balloonstyle != null)
      {
        writer.WriteStartElement("BalloonStyle");
        this.balloonstyle.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.liststyle != null)
      {
        writer.WriteStartElement("ListStyle");
        this.liststyle.WriteXml(writer);
        writer.WriteEndElement();
      }
    }

    #endregion
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the IconStyle section of KML
  /// </summary>
  public class IconStyle : IXmlSerializable
  {
    #region Private Member Variables

    /// <summary>
    /// The unique ID of this style.
    /// </summary>
    private string id;

    /// <summary>
    /// The color of the style.
    /// </summary>
    private string color;

    /// <summary>
    /// The color mode associated with this style.
    /// </summary>
    private ColorModeEnum? colorMode;

    /// <summary>
    /// Used to resize the icon.
    /// </summary>
    private float? scale;

    /// <summary>
    /// The direction in degrees. Can range from 0 to 360, where 0 is north. 
    /// </summary>
    private float? heading;

    /// <summary>
    /// The icon associated with this style.
    /// </summary>
    private KMLIcon icon;

    /// <summary>
    /// The position within the icon that is anchored to the point that the icon corresponds to. 
    /// </summary>
    private HotSpot hotSpot;
    #endregion

    #region Public Member Functions

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;
      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.color != null)
      {
        hasAValue = true;
      }

      if (this.colorMode != null)
      {
        hasAValue = true;
      }

      if (this.scale != null)
      {
        hasAValue = true;
      }

      if (this.heading != null)
      {
        hasAValue = true;
      }

      if (this.icon != null && this.icon.HasAValue())
      {
        hasAValue = true;
      }

      if (this.hotSpot != null && this.hotSpot.HasAValue())
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the IconStyle section of the KML 
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "IconStyle")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadIconstyleXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadIconstyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement || reader.LocalName == "hotSpot")
          {
            if (reader.LocalName == "colorMode")
            {
              bool canParse = false;
              ColorModeEnum val;
              string elementVal = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementVal, out val);

              if (canParse)
              {
                this.colorMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "color")
            {
              this.color = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "scale")
            {
              this.scale = KMLStyle.ParseFloat(KMLStyle.GetElementStringValue(reader.ReadSubtree()));
            }
            else if (reader.LocalName == "heading")
            {
              this.heading = KMLStyle.ParseFloat(KMLStyle.GetElementStringValue(reader.ReadSubtree()));
            }
            else if (reader.LocalName == "Icon")
            {
              this.icon = new KMLIcon();

              this.icon.ReadXml(reader);

              if (!this.icon.HasAValue())
              {
                this.icon = null;
              }
            }
            else if (reader.LocalName == "hotSpot")
            {
              this.hotSpot = new HotSpot();

              this.hotSpot.ReadXml(reader);

              if (!this.hotSpot.HasAValue())
              {
                this.hotSpot = null;
              }
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.color != null)
      {
        writer.WriteElementString("color", this.color);
      }

      if (this.colorMode != null)
      {
        writer.WriteElementString("colorMode", this.colorMode.ToString());
      }

      if (this.scale != null)
      {
        writer.WriteElementString("scale", this.scale.ToString());
      }

      if (this.heading != null)
      {
        writer.WriteElementString("heading", this.heading.ToString());
      }

      if (this.icon != null)
      {
        writer.WriteStartElement("Icon");
        this.icon.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.hotSpot != null)
      {
        writer.WriteStartElement("hotSpot");
        this.hotSpot.WriteXml(writer);
        writer.WriteEndElement();
      }
    }
    #endregion
  }

  /// <summary>
  /// Contains methods for serializing and deserializing information founnd in the Icon section of KML
  /// </summary>
  public class KMLIcon : IXmlSerializable
  {
    #region Private Member Variables

    /// <summary>
    /// the unique ID of this style.
    /// </summary>
    private string id;

    /// <summary>
    /// an HTTP address or path to load 
    /// </summary>
    private string href;

    /// <summary>
    /// If the href specifies an icon palette, x  identifies the x offset, in pixels, from the lower-left corner of the icon palette.
    /// </summary>
    private int? offsetX;

    /// <summary>
    /// If the href specifies an icon palette, y identifies the y offset, in pixels, from the lower-left corner of the icon palette.
    /// </summary>
    private int? offsetY;

    /// <summary>
    /// If the href specifies an icon palette, this variable specifies the width.
    /// </summary>
    private int? width;

    /// <summary>
    /// If the href specifies an icon palette, this variable specifies the height.
    /// </summary>
    private int? height;

    /// <summary>
    /// The RefreshMode associated with this style.
    /// </summary>
    private RefreshModeEnum? refreshMode;

    /// <summary>
    /// The RefreshMode associated with this style.
    /// </summary>
    private float? refreshInterval;

    /// <summary>
    /// The ViewRefreshModeEnum associated with this style.
    /// </summary>
    private ViewRefreshModeEnum? viewRefreshMode;

    /// <summary>
    /// The viewRefreshTime associated with this style.
    /// </summary>
    private float? viewRefreshTime;

    /// <summary>
    /// The amount by which hte bounding box should be scaled.
    /// </summary>
    private float? viewBoundScale;

    /// <summary>
    /// Contains optional viewing paramters as well as optoinal version and language information.
    /// </summary>
    private string viewFormat;

    /// <summary>
    /// Contiains the http query to be sent to the server. 
    /// </summary>
    private string httpQuery;
    #endregion

    #region Public Member Functions

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.href != null)
      {
        hasAValue = true;
      }

      if (this.offsetX != null)
      {
        hasAValue = true;
      }

      if (this.offsetY != null)
      {
        hasAValue = true;
      }

      if (this.width != null)
      {
        hasAValue = true;
      }

      if (this.height != null)
      {
        hasAValue = true;
      }

      if (this.refreshMode != null)
      {
        hasAValue = true;
      }

      if (this.refreshInterval != null)
      {
        hasAValue = true;
      }

      if (this.viewRefreshMode != null)
      {
        hasAValue = true;
      }

      if (this.viewRefreshTime != null)
      {
        hasAValue = true;
      }

      if (this.viewBoundScale != null)
      {
        hasAValue = true;
      }

      if (this.viewFormat != null)
      {
        hasAValue = true;
      }

      if (this.httpQuery != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the Icon section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "Icon")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadKMLIconstyleXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadKMLIconstyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "refreshMode")
            {
              bool canParse = false;
              RefreshModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.refreshMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "viewRefreshMode")
            {
              bool canParse = false;
              ViewRefreshModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.viewRefreshMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "href")
            {
              this.href = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              KMLStyle.SetHRef(ref this.href);
            }
            else if (reader.LocalName == "gx : x")
            {
              this.offsetX = KMLStyle.ParseInt(reader.Value);
            }
            else if (reader.LocalName == "gx : y")
            {
              this.offsetY = KMLStyle.ParseInt(reader.Value);
            }
            else if (reader.LocalName == "gx : h")
            {
              this.height = KMLStyle.ParseInt(reader.Value);
            }
            else if (reader.LocalName == "gx : w")
            {
              this.width = KMLStyle.ParseInt(reader.Value);
            }
            else if (reader.LocalName == "refreshInterval")
            {
              this.refreshInterval = KMLStyle.ParseFloat(reader.Value);
            }
            else if (reader.LocalName == "viewRefreshTime")
            {
              this.viewRefreshTime = KMLStyle.ParseFloat(reader.Value);
            }
            else if (reader.LocalName == "viewBoundScale")
            {
              this.viewBoundScale = KMLStyle.ParseFloat(reader.Value);
            }
            else if (reader.LocalName == "viewFormat")
            {
              this.viewFormat = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "httpQuery")
            {
              this.httpQuery = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.href != null)
      {
        writer.WriteElementString("href", this.href);
      }

      if (this.offsetX != null)
      {
        writer.WriteElementString("gx : x", this.offsetX.ToString());
      }

      if (this.offsetY != null)
      {
        writer.WriteElementString("gx : y", this.offsetY.ToString());
      }

      if (this.width != null)
      {
        writer.WriteElementString("gx : w", this.width.ToString());
      }

      if (this.height != null)
      {
        writer.WriteElementString("gx : h", this.height.ToString());
      }

      if (this.refreshMode != null)
      {
        writer.WriteElementString("refreshMode", this.refreshMode.ToString());
      }

      if (this.refreshInterval != null)
      {
        writer.WriteElementString("refreshInterval", this.refreshInterval.ToString());
      }

      if (this.viewRefreshMode != null)
      {
        writer.WriteElementString("viewRefreshMode", this.viewRefreshMode.ToString());
      }

      if (this.viewRefreshTime != null)
      {
        writer.WriteElementString("viewRefreshTime", this.viewRefreshTime.ToString());
      }

      if (this.viewBoundScale != null)
      {
        writer.WriteElementString("viewBoundScale", this.viewBoundScale.ToString());
      }

      if (this.viewFormat != null)
      {
        writer.WriteElementString("viewFormat", this.viewFormat);
      }

      if (this.httpQuery != null)
      {
        writer.WriteElementString("httpQuery", this.httpQuery);
      }
    }
    #endregion
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the HotSpot section of KML.
  /// </summary>
  public class HotSpot : IXmlSerializable
  {
    /// <summary>
    /// The value of the x offset.
    /// </summary>
    private string x;

    /// <summary>
    /// The value of the y offset.
    /// </summary>
    private string y;

    /// <summary>
    /// The way in which the x value is offset.  
    /// </summary>
    private HotSpotUnitEnum? xunits;

    /// <summary>
    /// The way in which the y value is offset.  
    /// </summary>
    private HotSpotUnitEnum? yunits;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.x != null)
      {
        hasAValue = true;
      }

      if (this.y != null)
      {
        hasAValue = true;
      }

      if (this.xunits != null)
      {
        hasAValue = true;
      }

      if (this.yunits != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the HotSpot section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "hotSpot")
      {
        int attributeCount = reader.AttributeCount;

        if (attributeCount == 4)
        {
          this.x = reader["x"];
          this.y = reader["y"];

          bool canParse = false;
          HotSpotUnitEnum val;
          canParse = KMLStyle.EnumTryParse(reader["xunits"], out val);
          if (canParse)
          {
            this.xunits = val;
          }
          else
          {
            // error
          }

          canParse = KMLStyle.EnumTryParse(reader["yunits"], out val);
          if (canParse)
          {
            this.yunits = val;
          }
          else
          {
            // error
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.x != null)
      {
        writer.WriteAttributeString("x", this.x);
      }

      if (this.y != null)
      {
        writer.WriteAttributeString("y", this.y);
      }

      if (this.xunits != null)
      {
        writer.WriteAttributeString("xunits", this.xunits.ToString());
      }

      if (this.yunits != null)
      {
        writer.WriteAttributeString("yunits", this.yunits.ToString());
      }
    }
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the LabelStyle section of KML
  /// </summary>
  public class LabelStyle : IXmlSerializable
  {
    /// <summary>
    /// The unique Id of this style.
    /// </summary>
    private string id;

    /// <summary>
    /// The color associated with this style. 
    /// </summary>
    private string color;

    /// <summary>
    /// The color mode associated with this style.
    /// </summary>
    private ColorModeEnum? colorMode;

    /// <summary>
    /// The amount by which the label should be scaled.
    /// </summary>
    private float? scale;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.color != null)
      {
        hasAValue = true;
      }

      if (this.colorMode != null)
      {
        hasAValue = true;
      }

      if (this.scale != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the LabelStyle section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "LabelStyle")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadLabelstyleXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML.
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadLabelstyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "colorMode")
            {
              bool canParse = false;
              ColorModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.colorMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "color")
            {
              this.color = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "scale")
            {
              this.scale = KMLStyle.ParseFloat(KMLStyle.GetElementStringValue(reader.ReadSubtree()));
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.color != null)
      {
        writer.WriteElementString("color", this.color);
      }

      if (this.colorMode != null)
      {
        writer.WriteElementString("colorMode", this.colorMode.ToString());
      }

      if (this.scale != null)
      {
        writer.WriteElementString("scale", this.scale.ToString());
      }
    }
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the LineStyle section of KML
  /// </summary>
  public class LineStyle : IXmlSerializable
  {
    /// <summary>
    /// The unique Id of this style.
    /// </summary>
    private string id;

    /// <summary>
    /// The color associated with this style. 
    /// </summary>
    private string color;

    /// <summary>
    /// The color mode associated with this style.
    /// </summary>
    private ColorModeEnum? colorMode;

    /// <summary>
    /// The width of the line.
    /// </summary>
    private float? width;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.color != null)
      {
        hasAValue = true;
      }

      if (this.colorMode != null)
      {
        hasAValue = true;
      }

      if (this.width != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the LineStyle section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "LineStyle")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadLinestyleXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML.
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadLinestyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "colorMode")
            {
              bool canParse = false;
              ColorModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.colorMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "color")
            {
              this.color = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "width")
            {
              this.width = KMLStyle.ParseFloat(KMLStyle.GetElementStringValue(reader.ReadSubtree()));
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.color != null)
      {
        writer.WriteElementString("color", this.color);
      }

      if (this.colorMode != null)
      {
        writer.WriteElementString("colorMode", this.colorMode.ToString());
      }

      if (this.width != null)
      {
        writer.WriteElementString("width", this.width.ToString());
      }
    }
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the PolyStyle section of KML
  /// </summary>
  public class PolyStyle : IXmlSerializable
  {
    /// <summary>
    /// The unique Id of this style.
    /// </summary>
    private string id;

    /// <summary>
    /// The color associated with this style. 
    /// </summary>
    private string color;

    /// <summary>
    /// The color mode associated with this style.
    /// </summary>
    private ColorModeEnum? colorMode;

    /// <summary>
    /// Whether the polygon should be filled or not. 
    /// </summary>
    private bool? fill;

    /// <summary>
    /// Whether the polygon should be outlined or not. 
    /// </summary>
    private bool? outline;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.color != null)
      {
        hasAValue = true;
      }

      if (this.colorMode != null)
      {
        hasAValue = true;
      }

      if (this.fill != null)
      {
        hasAValue = true;
      }

      if (this.outline != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the PolyStyle section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "PolyStyle")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadPolystyleXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadPolystyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "colorMode")
            {
              bool canParse = false;
              ColorModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.colorMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "color")
            {
              this.color = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "fill")
            {
              this.fill = XmlConvert.ToBoolean(KMLStyle.GetElementStringValue(reader.ReadSubtree()));
            }
            else if (reader.LocalName == "outline")
            {
              this.outline = XmlConvert.ToBoolean(KMLStyle.GetElementStringValue(reader.ReadSubtree()));
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.color != null)
      {
        writer.WriteElementString("color", this.color);
      }

      if (this.colorMode != null)
      {
        writer.WriteElementString("colorMode", this.colorMode.ToString());
      }

      if (this.fill != null)
      {
        writer.WriteElementString("fill", this.fill.Value ? "1" : "0");
      }

      if (this.outline != null)
      {
        writer.WriteElementString("outline", this.outline.Value ? "1" : "0");
      }
    }
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the BalloonStyle section of KML
  /// </summary>
  public class BalloonStyle : IXmlSerializable
  {
    /// <summary>
    /// The unique ID associated with this style.
    /// </summary>
    private string id;

    /// <summary>
    /// The background color of the balloon.
    /// </summary>
    private string backgroundColor;

    /// <summary>
    /// The color of the text within the balloon.
    /// </summary>
    private string textColor;

    /// <summary>
    /// The text contained inside the balloon.
    /// </summary>
    private string text;

    /// <summary>
    /// The display mode associated with this style.
    /// </summary>
    private DisplayModeEnum? displayMode;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.backgroundColor != null)
      {
        hasAValue = true;
      }

      if (this.textColor != null)
      {
        hasAValue = true;
      }

      if (this.text != null)
      {
        hasAValue = true;
      }

      if (this.displayMode != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the BalloonStyle section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "BalloonStyle")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();

          this.ReadBalloonstyleXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadBalloonstyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "displayMode")
            {
              bool canParse = false;
              DisplayModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.displayMode = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "bgColor")
            {
              this.backgroundColor = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "textColor")
            {
              this.textColor = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "text")
            {
              this.text = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.backgroundColor != null)
      {
        writer.WriteElementString("bgColor", this.backgroundColor);
      }

      if (this.textColor != null)
      {
        writer.WriteElementString("textColor", this.textColor);
      }

      if (this.text != null)
      {
        writer.WriteElementString("text", this.text);
      }

      if (this.displayMode != null)
      {
        writer.WriteElementString("displayMode", this.displayMode.ToString());
      }
    }
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the ListStyle section of KML
  /// </summary>
  public class ListStyle : IXmlSerializable
  {
    /// <summary>
    /// The unique ID of this style.
    /// </summary>
    private string id;

    /// <summary>
    /// The list item type associated with this style.
    /// </summary>
    private ListItemTypeEnum? listItemType;

    /// <summary>
    /// The background color of the list.
    /// </summary>
    private string backgroundColor;

    /// <summary>
    /// The icon associated with this list. 
    /// </summary>
    private ItemIcon icon;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.id != null)
      {
        hasAValue = true;
      }

      if (this.listItemType != null)
      {
        hasAValue = true;
      }

      if (this.backgroundColor != null)
      {
        hasAValue = true;
      }

      if (this.icon != null && this.icon.HasAValue())
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the ListStyle section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "ListStyle")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          this.id = reader["id"];

          XmlReader innerReader = reader.ReadSubtree();
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadListstyleXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "listItemType")
            {
              bool canParse = false;
              ListItemTypeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());
              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.listItemType = val;
              }
              else
              {
                // error
              }
            }
            else if (reader.LocalName == "bgColor")
            {
              this.backgroundColor = KMLStyle.GetElementStringValue(reader.ReadSubtree());
            }
            else if (reader.LocalName == "ItemIcon")
            {
              this.icon = new ItemIcon();
              this.icon.ReadXml(reader);
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.id != null)
      {
        writer.WriteAttributeString("id", this.id);
      }

      if (this.listItemType != null)
      {
        writer.WriteElementString("listItemType", this.listItemType.ToString());
      }

      if (this.backgroundColor != null)
      {
        writer.WriteElementString("bgColor", this.backgroundColor);
      }

      if (this.icon != null)
      {
        writer.WriteStartElement("ItemIcon");
        this.icon.WriteXml(writer);
        writer.WriteEndElement();
      }
    }
  }

  /// <summary>
  /// Contains methods for serializing and deserializing the XML found in the ItemIcon section of KML
  /// </summary>
  public class ItemIcon : IXmlSerializable
  {
    /// <summary>
    /// The ItemIconMoe associated with this style. 
    /// </summary>
    private ItemIconModeEnum? state;

    /// <summary>
    /// an HTTP address or path to load 
    /// </summary>
    private string href;

    /// <summary>
    /// Determines if this class has any styling information associated with it. 
    /// </summary>
    /// <returns>True if at least one of the fields that contains style information has a value, otherwise false.</returns>
    public bool HasAValue()
    {
      bool hasAValue = false;

      if (this.state != null)
      {
        hasAValue = true;
      }

      if (this.href != null)
      {
        hasAValue = true;
      }

      return hasAValue;
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema()
    {
      return null;
    }

    /// <summary>
    /// Reads the xml in the ItemIcon section of the KML 
    /// </summary>
    /// <param name="reader">Used to read the XML.</param>
    public void ReadXml(XmlReader reader)
    {
      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "ItemIcon")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          XmlReader innerReader = reader.ReadSubtree();

          this.ReadItemIconXML(innerReader);
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML
    /// </summary>
    /// <param name="reader">Reads the xml</param>
    public void ReadItemIconXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          if (reader.LocalName == "state")
          {
            isEmptyElement = reader.IsEmptyElement;

            if (!isEmptyElement)
            {
              bool canParse = false;
              ItemIconModeEnum val;
              string elementValue = KMLStyle.GetElementStringValue(reader.ReadSubtree());
              canParse = KMLStyle.EnumTryParse(elementValue, out val);

              if (canParse)
              {
                this.state = val;
              }
              else
              {
                // error
              }
            }
          }
          else if (reader.LocalName == "href")
          {
            if (!isEmptyElement)
            {
              this.href = KMLStyle.GetElementStringValue(reader.ReadSubtree());

              KMLStyle.SetHRef(ref this.href);
            }
          }
        }
      }
    }

    /// <summary>
    /// Serializes this class to XML.
    /// </summary>
    /// <param name="writer">Performs the serialization.</param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.state != null)
      {
        writer.WriteElementString("state", this.state.ToString());
      }

      if (this.href != null)
      {
        writer.WriteElementString("href", this.href);
      }
    }
  }
}
